/**
 * 检查参数是否为合法数值或参数转换为number类型后是否为合法数值
 * @param arg 参数类型为 number或string
 * @return boolean true / false
 * @since V1.2.0
 * @example
 * ```
 * isValidNumber(100)        // true
 * isValidNumber('-100')     // true
 * isValidNumber('')         // true
 * isValidNumber(null)       // true
 * isValidNumber(undefined)  // true
 * isValidNumber('x100')     // false
 * ```
 */
export function isValidNumber(arg: number | string): boolean {
    const value = Number(arg);
    return !isNaN(value) && isFinite(value);
}

/**
 * 检查参数是否为数字或数字字符串，返回number类型的值
 * 1. 如果 `transFlag` 值为 `true` 且参数值转换为Number类型后是 `NaN` ，则将参数值转换为数字 `0` ，否则不做转换；
 * 2. 判断上一步处理后的参数值是否为正数、负数、小数，如果是则将参数返回，否则抛出 `TypeError`。
 * @param arg 要校验的数值
 * @param transFlag boolean，默认值为true，是否需要将无法转换成数字类型的参数值设置为0
 * @returns number or TypeError
 * @version V1.1.0
 * @since V1.0.0
 */
export function numberCheck(arg: any, transFlag = true): number {
    let value = Number(arg);
    if (isNaN(value) && !transFlag) {
        throw new TypeError(`参数值在转换成数字时发生错误: [${JSON.stringify(arg)}]!`);
    } else if (isNaN(value) && transFlag) {
        value = 0;
    }
    if (new RegExp(/^(-|\+)?\d+(\.\d+)?$/).test(value.toString())) { // 正则匹配正数、负数、小数
        return value;
    } else {
        throw new TypeError(`参数值在进行数字检查时发生错误: [${JSON.stringify(value)}]!`);
    }
}

/**
 * 两个数字相加
 * @param arg1
 * @param arg2
 * @returns 两数之和 {number}
 * @since V1.0.0
 */
export function add(arg1: any, arg2: any): number {
    arg1 = numberCheck(arg1);
    arg2 = numberCheck(arg2);

    let digit1, digit2; // 小数位数
    try {
        digit1 = arg1.toString().split('.')[1].length;
    } catch (e) {
        digit1 = 0;
    }
    try {
        digit2 = arg2.toString().split('.')[1].length;
    } catch (e) {
        digit2 = 0;
    }
    // 计算倍数
    const multiple = Math.pow(10, Math.max(digit1, digit2) + 1);
    return Math.round(arg1 * multiple + arg2 * multiple) / multiple;
}

/**
 * 多参数 加法运算
 * @param args 参数列表 {any[]}
 * @returns 所有参数的和 {number}
 * @since V1.0.0
 */
export function calcAdd(args: any[]): number {
    let sum = 0;
    args.forEach(p => {
        sum = add(sum, p);
    });
    return sum;
}

/**
 * 两个数字相减
 * @param arg1 被减数
 * @param arg2 减数
 * @returns number类型的差值 {number}
 * @since V1.0.0
 */
export function calcSub(arg1: any, arg2: any): number {
    arg1 = numberCheck(arg1);
    arg2 = numberCheck(arg2);

    let digit1, digit2; // 小数位数
    try {
        digit1 = arg1.toString().split('.')[1].length;
    } catch (e) {
        digit1 = 0;
    }
    try {
        digit2 = arg2.toString().split('.')[1].length;
    } catch (e) {
        digit2 = 0;
    }
    // 计算倍数
    const multiple = Math.pow(10, Math.max(digit1, digit2));
    // 动态控制精度长度
    const maxDigit = (digit1 >= digit2) ? digit1 : digit2;
    const result = ((arg1 * multiple - arg2 * multiple) / multiple).toFixed(maxDigit);
    return Number(result);
}


/**
 * 两数求积
 * @param arg1
 * @param arg2
 * @returns 两数乘积 {number}
 * @since V1.0.0
 */
export function calcMul(arg1: any, arg2: any): number {

    arg1 = numberCheck(arg1);
    arg2 = numberCheck(arg2);

    if (!arg1 || !arg2) {
        return 0;
    }
    let digit = 0; // 小数位数
    const s1 = arg1.toString(), s2 = arg2.toString();

    try {
        if (s1.split('.')[1]) {
            digit += s1.split('.')[1].length;
        }
        if (s2.split('.')[1]) {
            digit += s2.split('.')[1].length;
        }
    } catch (e) {
        console.error(e);
        throw new Error('An error occurred in the method [calcMul].');
    }
    return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, digit);
}

/**
 * 两数求商
 * @param arg1
 * @param arg2
 * @returns 两数的商
 * @since V1.0.0
 */
export function calcDiv(arg1: any, arg2: any): number {
    arg1 = numberCheck(arg1);
    arg2 = numberCheck(arg2);

    let digit1, digit2;
    try {
        digit1 = arg1.toString().split('.')[1].length;
    } catch (err) {
        digit1 = 0;
    }
    try {
        digit2 = arg2.toString().split('.')[1].length;
    } catch (err) {
        digit2 = 0;
    }
    const intNum1 = Number(arg1.toString().replace('.', ''));
    const intNum2 = Number(arg2.toString().replace('.', ''));
    return calcMul(intNum1 / intNum2, Math.pow(10, digit2 - digit1));
}

/** Number类型数据相关方法 */
export class NumberUtils {

    /** number数字检查 */
    static numberCheck(arg: any) {
        return numberCheck(arg);
    }

    /** 两数求和 */
    static add(arg1: any, arg2: any) {
        return add(arg1, arg2);
    }

    /** 数字数组求和 */
    static calcAdd(args: any[]): number {
        return calcAdd(args);
    }

    /** 两数相减 */
    static calcSub(arg1: any, arg2: any): number {
        return calcSub(arg1, arg2);
    }

    /** 两数相乘 */
    static calcMul(arg1: any, arg2: any): number {
        return calcMul(arg1, arg2);
    }

    /** 两数相除 */
    static calcDiv(arg1: any, arg2: any): number {
        return calcDiv(arg1, arg2);
    }

    /** 检查参数是否为合法数值 */
    static isValidNumber(arg: number | string): boolean {
        return isValidNumber(arg);
    }
}
